<template>
	<div class="chatgpt-type-effect" :class="{ 'is-done': isDone }">
		<div class="content-wrapper">
			<div class="content" ref="contentRef"></div>
			<span v-show="showCursor && !isDone" class="cursor"></span>
		</div>
	</div>
</template>

<script>
export default {
	name: 'TypeEffect',
	props: {
		text: {
			type: String,
			required: true,
		},
		speed: {
			type: Number,
			default: 30,
		},
		startDelay: {
			type: Number,
			default: 0,
		},
		showCursor: {
			type: Boolean,
			default: true,
		},
		html: {
			type: Boolean,
			default: false,
		},
	},
	data() {
		return {
			displayedText: '',
			isTyping: false,
			isDone: false,
			currentIndex: 0,
			timer: null,
			previousTextLength: 0,
			cursorStyle: {
				left: '0px',
			},
		}
	},
	watch: {
		text: {
			handler(newText, oldText) {
				// 检查是否有新内容添加
				if (newText && newText !== oldText) {
					// 如果当前正在打字，先停止
					if (this.isTyping) {
						clearTimeout(this.timer)
						this.isTyping = false
					}

					// 保存之前文本的长度
					this.previousTextLength = oldText ? oldText.length : 0

					// 如果之前已经完成了打字，重置 isDone 状态
					if (this.isDone) {
						this.isDone = false
					}

					// 从新的起始位置开始打字
					this.currentIndex = this.previousTextLength
					this.startTyping()
				} else if (!newText) {
					// 如果文本被清空，重置组件
					this.reset()
				}
			},
		},
	},
	methods: {
		startTyping() {
			if (this.isTyping) return

			this.isTyping = true
			this.isDone = false

			// 如果是从头开始，添加延迟；如果是继续打字，立即开始
			const delay = this.currentIndex === 0 ? this.startDelay : 0

			setTimeout(() => {
				this.typeNextChar()
			}, delay)
		},
		typeNextChar() {
			if (this.currentIndex >= this.text.length) {
				this.isTyping = false
				this.isDone = true
				this.$emit('typing-done')
				return
			}

			this.currentIndex++
			const textToShow = this.text.substring(0, this.currentIndex)

			if (this.html) {
				this.$refs.contentRef.innerHTML = textToShow
			} else {
				this.$refs.contentRef.textContent = textToShow
			}

			// 更新光标位置
			this.$nextTick(() => {
				this.updateCursorPosition()
			})

			this.timer = setTimeout(() => {
				this.typeNextChar()
			}, this.speed)
		},
		reset() {
			clearTimeout(this.timer)
			this.currentIndex = 0
			this.previousTextLength = 0
			this.isTyping = false
			this.isDone = false
			if (this.$refs.contentRef) {
				if (this.html) {
					this.$refs.contentRef.innerHTML = ''
				} else {
					this.$refs.contentRef.textContent = ''
				}
			}
		},
		// 添加一个方法来追加文本
		appendText(newText) {
			if (!newText) return

			// 更新 props 中的 text
			this.$emit('update:text', (this.text || '') + newText)
		},
		// 添加新方法来更新光标位置
		updateCursorPosition() {
			const contentEl = this.$refs.contentRef
			if (!contentEl) return

			// 创建一个临时的 span 元素来测量文本宽度
			const tempSpan = document.createElement('span')
			tempSpan.style.visibility = 'hidden'
			tempSpan.style.position = 'absolute'
			tempSpan.style.whiteSpace = 'pre-wrap'
			tempSpan.style.font = window.getComputedStyle(contentEl).font
			tempSpan.textContent = contentEl.textContent || contentEl.innerHTML

			document.body.appendChild(tempSpan)
			const textWidth = tempSpan.getBoundingClientRect().width
			document.body.removeChild(tempSpan)

			// 更新光标位置
			this.cursorStyle = {
				left: `${textWidth}px`,
			}
		},
	},
	beforeUnmount() {
		clearTimeout(this.timer)
	},
}
</script>

<style scoped>
.chatgpt-type-effect {
	position: relative;
	display: inline-block;
	font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell,
		'Open Sans', 'Helvetica Neue', sans-serif;
	line-height: 1.5;
	/* 添加以下属性优化文字渲染 */
	text-rendering: optimizeLegibility;
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
	will-change: contents;
}

.content-wrapper {
	position: relative;
	display: inline;
}

.cursor {
	display: inline-block;
	width: 2px;
	height: 1.2em;
	background-color: currentColor;
	margin-left: 1px;
	vertical-align: middle;
	/* 优化光标闪烁动画 */
	animation: blink 1s cubic-bezier(0.68, -0.55, 0.27, 1.55) infinite;
	transform-origin: center;
	will-change: opacity;
}

@keyframes blink {
	0% {
		opacity: 1;
	}
	50% {
		opacity: 0.3;
	}
	100% {
		opacity: 1;
	}
}

/* .content {
	display: inline;
	white-space: pre-wrap;
	backface-visibility: hidden;
	transform: translateZ(0);
} */
.content {
	opacity: 0;
	animation: fadeIn 1.5s ease-in-out forwards;
}

@keyframes fadeIn {
	from {
		opacity: 0;
		transform: translateY(1px);
	}
	to {
		opacity: 1;
		transform: translateY(0);
	}
}
</style>
